#include "statuswidget.h"
#include "topbuttons.h"
#ifdef USE_ASSIMP_FOR_3D_MODEL_VIEWER
#include "modelviewwidget.h"
#endif
#include "mavlinkdataprocessor.h"

QMutex StatusWidget::mutex;
StatusWidget *StatusWidget::instance = NULL;

StatusWidget::StatusWidget(QWidget *parent) : QWidget(parent)
{
    vBoxLayoutPage = new QVBoxLayout;// 当前页面最外层的纵向布局
    topButtons = new TopButtons(QString::fromStdString(Pages::pagesString[Pages::StatusPage]));
    spacerUnderTopButtons = new QSpacerItem(0,0,QSizePolicy::Fixed,QSizePolicy::Expanding);
    vBoxLayoutModelView = new QVBoxLayout;
    modelViewWidget = new QWidget;

    hBoxLayoutMavlinkAndModel = new QHBoxLayout;// 除了顶部按钮之外的布局
    scrollAreaMavlinkMessages = new QScrollArea;// 全部MavLink消息列表
    scrollAreaContentMavlinkMessages = new QWidget;
    vBoxLayoutMavlinkMessages = new QVBoxLayout;
    vBoxLayoutMessageAndModel = new QVBoxLayout;// 右侧布局
    scrollAreaMessageDetail = new QScrollArea;// MavLink消息详细内容
    scrollAreaContentMessageDetail = new QWidget;
    vBoxLayoutMessageDetail = new QVBoxLayout;

    // 当前界面最外层布局
    vBoxLayoutPage->setMargin(0);// 每一个layout都要设置间距为0
    vBoxLayoutPage->setSpacing(0);

    // 添加顶部按钮
    vBoxLayoutPage->addLayout(topButtons);
//    vBoxLayoutPage->addSpacerItem(spacerUnderTopButtons);

    vBoxLayoutMavlinkMessages->setMargin(0);
    vBoxLayoutMavlinkMessages->setSpacing(0);
    scrollAreaContentMavlinkMessages->setLayout(vBoxLayoutMavlinkMessages);
    scrollAreaMavlinkMessages->setWidgetResizable(false);

    buttonToggleMavlinkMessageUpdate = new QPushButton(" MAVLINK MESSAGE UPDATE ENABLED");
    buttonToggleMavlinkMessageUpdate->setMinimumHeight(40);
    buttonToggleMavlinkMessageUpdate->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
    buttonToggleMavlinkMessageUpdate->setStyleSheet("QPushButton{text-align : left;}");
    buttonToggleMavlinkMessageUpdate->setCheckable(true);
    enableMavlinkMessageUpdate = true;
    vBoxLayoutMavlinkMessages->addWidget(buttonToggleMavlinkMessageUpdate);
    connect(buttonToggleMavlinkMessageUpdate, &QPushButton::clicked,
            this, &StatusWidget::toggleMavlinkMessageUpdateState);
    buttonToggleMavlinkMessageUpdate->setChecked(true);
    scrollAreaContentMavlinkMessages->adjustSize();
    scrollAreaMavlinkMessages->ensureWidgetVisible(buttonToggleMavlinkMessageUpdate);


    scrollAreaMavlinkMessages->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    scrollAreaMavlinkMessages->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    scrollAreaMavlinkMessages->setStyleSheet("QAbstractScrollArea{border: none;}"
                                    "QScrollArea{background-color:#F1F4FF;}");// #F1F4FF #EFFCFF transparent
    QScrollBar* scrBar = scrollAreaMavlinkMessages->verticalScrollBar();
    int scaleFactor = 1;
    scrBar->setFixedWidth(10 * scaleFactor);
    scrBar->setStyleSheet(QString("QScrollBar:vertical{"
                                  "width:%5px;background-color:#F2F3F9;"
                                  "margin:0px,0px,0px,0px;"
                                  "padding-top:0px;padding-bottom:0px;padding-right:%1px;}"
                                  "QScrollBar::handle:vertical{"
                                  "border-radius: %2px;width:%3px;"
                                  "background: rgb(0, 0, 0, 20);min-height:%4px;}"
                                  "QScrollBar::handle:vertical:hover{"
                                  "border-radius: %2px;width:%3px;"
                                  "background:rgb(0, 0, 0, 30);min-height:%4px;}"
                                  "QScrollBar::handle:vertical:pressed{"
                                  "border-radius: %2px;width:%3px;"
                                  "background:rgb(0, 0, 0, 40);min-height:%4px;}"
                                  "QScrollBar::sub-line:vertical, QScrollBar::add-line:vertical"
                                  "{width: 0px;height: 0px; background:#ffffff}"
                                  "QScrollBar::add-page:vertical,QScrollBar::sub-page:vertical"
                                  "{background: #ffffff;}")
                          .arg(0*scaleFactor).arg(int(4*scaleFactor)).arg(10*scaleFactor).arg(int(20*scaleFactor)).arg(int(10*scaleFactor)));

    scrollAreaMavlinkMessages->setWidget(scrollAreaContentMavlinkMessages);

    hBoxLayoutMavlinkAndModel->setMargin(0);
    hBoxLayoutMavlinkAndModel->setSpacing(0);
    hBoxLayoutMavlinkAndModel->addWidget(scrollAreaMavlinkMessages);
    hBoxLayoutMavlinkAndModel->setStretchFactor(scrollAreaMavlinkMessages, 2);

    vBoxLayoutMessageDetail->setMargin(0);
    vBoxLayoutMessageDetail->setSpacing(0);
    scrollAreaContentMessageDetail->setLayout(vBoxLayoutMessageDetail);
    MavlinkMessageField *mavlinkMessageField = new MavlinkMessageField("null", "null");
    vBoxLayoutMessageDetail->addLayout(mavlinkMessageField);

    scrollAreaMessageDetail->setWidgetResizable(false);
    scrollAreaMessageDetail->setWidget(scrollAreaContentMessageDetail);

    timerUpdateMavlinkMessageDetail = new QTimer;
    timerUpdateMavlinkMessageDetail->setInterval(50);// UI update by 20Hz
    timerUpdateMavlinkMessageDetail->setSingleShot(true);
    connect(timerUpdateMavlinkMessageDetail, &QTimer::timeout,
            this, &StatusWidget::updateMavlinkMessageDetailData);

    timerUpdateMavlinkMessageFrequency = new QTimer;
    timerUpdateMavlinkMessageFrequency->setInterval(1000);
    connect(timerUpdateMavlinkMessageFrequency, &QTimer::timeout,
            this, &StatusWidget::UpdateMavlinkMessageFrequency);
    timerUpdateMavlinkMessageFrequency->start();

    vBoxLayoutMessageAndModel->setMargin(0);
    vBoxLayoutMessageAndModel->setSpacing(0);
    vBoxLayoutMessageAndModel->addWidget(scrollAreaMessageDetail);
    vBoxLayoutMessageAndModel->setStretchFactor(scrollAreaMessageDetail, 2);


    // 添加modelview
    vBoxLayoutModelView->setMargin(0);
    vBoxLayoutModelView->setSpacing(0);
    modelViewWidget->setLayout(vBoxLayoutModelView);
    vBoxLayoutMessageAndModel->addWidget(modelViewWidget);
    vBoxLayoutMessageAndModel->setStretchFactor(modelViewWidget, 2);
    hBoxLayoutMavlinkAndModel->addLayout(vBoxLayoutMessageAndModel);
    hBoxLayoutMavlinkAndModel->setStretchFactor(vBoxLayoutMessageAndModel, 3);

    vBoxLayoutPage->addLayout(hBoxLayoutMavlinkAndModel);
    this->setLayout(vBoxLayoutPage);

#ifndef USE_ASSIMP_FOR_3D_MODEL_VIEWER
    // 添加label
    labelModelViewCameraPosition = new QLabel;

    stringLabelModelViewCameraPosition = "Qt project file closd assimp use";
    labelModelViewCameraPosition->setText(stringLabelModelViewCameraPosition);
    vBoxLayoutModelView->addWidget(labelModelViewCameraPosition);
#endif
}

void StatusWidget::resizeEvent(QResizeEvent *event)
{
    scrollAreaContentMavlinkMessages->resize(scrollAreaMavlinkMessages->size().width()
                                             - scrollAreaMavlinkMessages->verticalScrollBar()->size().width(),
                                             scrollAreaContentMavlinkMessages->size().height());
    scrollAreaContentMavlinkMessages->setMinimumWidth(scrollAreaMavlinkMessages->size().width()
                        - scrollAreaMavlinkMessages->verticalScrollBar()->size().width());

    scrollAreaContentMessageDetail->resize(scrollAreaMessageDetail->size().width()
                                           - scrollAreaMessageDetail->verticalScrollBar()->size().width(),
                                           scrollAreaContentMessageDetail->size().height());
}

void StatusWidget::addModelViewWidget()
{
    LOGD("here");
#ifdef USE_ASSIMP_FOR_3D_MODEL_VIEWER
    labelModelViewCameraPosition = new QLabel;
    ModelViewWidget *widget = new ModelViewWidget("../resource/airplane2.obj");
    vBoxLayoutModelView->addWidget(widget);

    // 添加label
    widget->getCameraPosition(camerax, cameray, cameraz);
    stringLabelModelViewCameraPosition = "camerax:" + QString::number(camerax)
            + " cameray:" + QString::number(cameray)
            + " cameraz:" + QString::number(cameraz);
    labelModelViewCameraPosition->setText(stringLabelModelViewCameraPosition);
    vBoxLayoutModelView->addWidget(labelModelViewCameraPosition);

    connect(widget, &ModelViewWidget::clicked, this, &StatusWidget::updateStringModelViewCameraPosition);
#else
    // do nothing
#endif
}

void StatusWidget::removeModelViewWidget()
{
    LOGD("here");
#ifdef USE_ASSIMP_FOR_3D_MODEL_VIEWER
    while (QLayoutItem* item = vBoxLayoutModelView->takeAt(0))
    {
        if (QWidget* widget = item->widget()){
            widget->setParent(nullptr);
            delete widget;
        }
        delete item;
    }
#else
    // do nothing
#endif
}

void StatusWidget::updateStringModelViewCameraPosition()
{
#ifdef USE_ASSIMP_FOR_3D_MODEL_VIEWER
    for (int i = vBoxLayoutModelView->layout()->count()-1; i >= 0; i--)
    {
        QLayoutItem *item = vBoxLayoutModelView->layout()->itemAt(i);
        ModelViewWidget *currentWidget = qobject_cast<ModelViewWidget *>(item->widget());
        if (currentWidget != nullptr)
        {
            currentWidget->getCameraPosition(camerax, cameray, cameraz);
        }
    }
    stringLabelModelViewCameraPosition = "camerax:" + QString::number(camerax)
            + " cameray:" + QString::number(cameray)
            + " cameraz:" + QString::number(cameraz);
    labelModelViewCameraPosition->setText(stringLabelModelViewCameraPosition);
#else
    // do nothing
#endif
}

void StatusWidget::addMavlinkMessage(mavlink_message_t *msg)
{
    int insertPosition = -1;
    MavlinkMessage *mavlinkMessage = new MavlinkMessage(msg);
    LOGI("new mavlink message:%s", mavlinkMessage->objectName().toStdString().data());
    for(int i = 0; i < vBoxLayoutMavlinkMessages->count(); i++)
    {
        QLayoutItem * item = vBoxLayoutMavlinkMessages->itemAt(i);
        MavlinkMessage *m = qobject_cast<MavlinkMessage*>(item->widget());
        if(m == nullptr){
            LOGD("currentWidget is not MavlinkMessage!");
            continue;
        }
        if(m->name > mavlinkMessage->name)
        {
            insertPosition = i;
            LOGD("insertPosition is:%d", insertPosition);
            break;
        }
    }
    if(insertPosition != -1){
        vBoxLayoutMavlinkMessages->insertWidget(insertPosition, mavlinkMessage);
    }else{
        vBoxLayoutMavlinkMessages->addWidget(mavlinkMessage);
    }
    connect(mavlinkMessage, &QPushButton::clicked,this, &StatusWidget::updateMavlinkMessageDetailLayout);
    scrollAreaContentMavlinkMessages->adjustSize();
    scrollAreaMavlinkMessages->ensureWidgetVisible(mavlinkMessage);
}

QWidget* StatusWidget::findMavlinkMessage(quint8 componentId, quint32 messageId)
{
    QList<MavlinkMessage *> messages = scrollAreaContentMavlinkMessages->findChildren<MavlinkMessage *>();
    for(int i=0; i < messages.count(); i++)
    {
        MavlinkMessage *m = messages.at(i);
//        qDebug() << m->componentId << componentId << m->messageId << messageId;
        if(m->componentId == componentId && m->messageId == messageId)
        {
//            qDebug() << "find mavlink message for messageId:" << messageId;
            return m;
        }
    }
    return nullptr;
}

// 更新MavLink消息标题
void StatusWidget::updateMavlinkMessage(mavlink_message_t msg)
{
    if(enableMavlinkMessageUpdate != true){
        return;
    }
    MavlinkMessage *mavlinkMessage = qobject_cast<MavlinkMessage*>(findMavlinkMessage(msg.compid, msg.msgid));
    if(mavlinkMessage != nullptr){
//        qDebug() << "will not create new message";
        // update it
        mavlinkMessage->lock.lockForWrite();
        mavlinkMessage->count++;
        mavlinkMessage->lastTime = mavlinkMessage->time;
        mavlinkMessage->time = QTime::currentTime();
        mavlinkMessage->message = msg;
        mavlinkMessage->lock.unlock();
        if(msg.compid == checkedMavlinkComponentId && msg.msgid == checkedMavlinkMessageId)
        {
//            qDebug() << "will update message detail: " << checkedMavlinkMessageId;
            // update UI after 50ms
            if(!timerUpdateMavlinkMessageDetail->isActive()){
                timerUpdateMavlinkMessageDetail->start();
            }
        }
    }else{
        // create a new one
        LOGI("add new message now");
        addMavlinkMessage(&msg);
    }
    return;
}

// 按钮按下时更新MavLink消息内容的UI布局
void StatusWidget::updateMavlinkMessageDetailLayout()
{
    MavlinkMessage* mavlinkMessage = (MavlinkMessage*)sender();
    rwlockMavlinkMessageDetail.lockForWrite();
    quint8 compid = mavlinkMessage->componentId;
    quint32 msgid = mavlinkMessage->messageId;

    MavlinkMessage *m = qobject_cast<MavlinkMessage*>(
                findMavlinkMessage(checkedMavlinkComponentId, checkedMavlinkMessageId));
    if(m != nullptr){
        m->setChecked(false);
    }
    mavlinkMessage->setChecked(true);
    checkedMavlinkComponentId = compid;
    checkedMavlinkMessageId = msgid;
    LOGI("will show compid:%d msgid:%d", compid, msgid);

    // update UI layout

    // remove last layout
    QLayoutItem *item;
    while ((item = vBoxLayoutMessageDetail->takeAt(0)) != 0)
    {
        // setParent为NULL，防止删除之后界面不消失
        if(item->widget())
        {
            item->widget()->setParent(NULL);
        }
        delete item;
    }

    // add new layout

    // get message info
    const mavlink_message_info_t* msgInfo = mavlink_get_message_info(&mavlinkMessage->message);
    if (!msgInfo) {
        LOGE("MAVLinkMessage NULL msgInfo msgid(%d)", checkedMavlinkMessageId);
        qDebug() << QStringLiteral("MAVLinkMessage NULL msgInfo msgid(%1)").arg(checkedMavlinkMessageId);
        rwlockMavlinkMessageDetail.unlock();
        return;
    }

    for (unsigned int i = 0; i < msgInfo->num_fields; ++i) {
        QString type = QString("?");
        switch (msgInfo->fields[i].type) {
        case MAVLINK_TYPE_CHAR:     type = QString("char");     break;
        case MAVLINK_TYPE_UINT8_T:  type = QString("uint8_t");  break;
        case MAVLINK_TYPE_INT8_T:   type = QString("int8_t");   break;
        case MAVLINK_TYPE_UINT16_T: type = QString("uint16_t"); break;
        case MAVLINK_TYPE_INT16_T:  type = QString("int16_t");  break;
        case MAVLINK_TYPE_UINT32_T: type = QString("uint32_t"); break;
        case MAVLINK_TYPE_INT32_T:  type = QString("int32_t");  break;
        case MAVLINK_TYPE_FLOAT:    type = QString("float");    break;
        case MAVLINK_TYPE_DOUBLE:   type = QString("double");   break;
        case MAVLINK_TYPE_UINT64_T: type = QString("uint64_t"); break;
        case MAVLINK_TYPE_INT64_T:  type = QString("int64_t");  break;
        }
        MavlinkMessageField *mavlinkMessageField = new MavlinkMessageField(msgInfo->fields[i].name, type);
        vBoxLayoutMessageDetail->addLayout(mavlinkMessageField);

        scrollAreaContentMessageDetail->adjustSize();
        // TODO: dynamic size
        scrollAreaContentMessageDetail->resize(scrollAreaMessageDetail->size().width()
                                               - 20,24*(i+1) );
        scrollAreaMessageDetail->ensureWidgetVisible(scrollAreaContentMessageDetail);
    }
    rwlockMavlinkMessageDetail.unlock();

    // 控件布局更新完成后，更新一次消息详细内容
    if(!timerUpdateMavlinkMessageDetail->isActive()){
        timerUpdateMavlinkMessageDetail->start();
    }
}

// 以最大20Hz更新MavLink消息详细内容，无新消息时不更新
void StatusWidget::updateMavlinkMessageDetailData()
{
    // 通过读写锁保证控件布局已经更新完成，才更新数据
    rwlockMavlinkMessageDetail.lockForWrite();
    // 获取当前需要显示的消息
    MavlinkMessage *mavlinkMessage = qobject_cast<MavlinkMessage*>(
                findMavlinkMessage(checkedMavlinkComponentId, checkedMavlinkMessageId));
    // 获取MavLink消息包
    mavlinkMessage->lock.lockForRead();
    mavlink_message_t msg = mavlinkMessage->message;
    mavlinkMessage->lock.unlock();

    // 解析并更新UI
    const mavlink_message_info_t* msgInfo = mavlink_get_message_info(&msg);
    if (!msgInfo) {
        qWarning() << QStringLiteral("updateMavlinkMessageDetailData: NULL msgInfo msgid(%1)").arg(msg.msgid);
        rwlockMavlinkMessageDetail.unlock();
        return;
    }

    QLayoutItem *item;
    int count = vBoxLayoutMessageDetail->count();

    if(count != static_cast<int>(msgInfo->num_fields)) {
        LOGE("msgInfo field:%d != count:%d msgid:%d", msgInfo->num_fields, count, msg.msgid);
        qWarning() << QStringLiteral("updateMavlinkMessageDetailData: msgInfo field count mismatch msgid(%1)").arg(msg.msgid);
        qDebug() << count << "!=" << msgInfo->num_fields;
        rwlockMavlinkMessageDetail.unlock();
        return;
    }

    uint8_t* m = reinterpret_cast<uint8_t*>(&msg.payload64[0]);
    MavlinkMessageField *field;
    for (unsigned int i = 0; i < msgInfo->num_fields; ++i) {
        field = nullptr;
        if((item = vBoxLayoutMessageDetail->itemAt(i)) != 0)
        {
            field = qobject_cast<MavlinkMessageField*>(item->layout());
        }
        if(field != nullptr) {
            const unsigned int offset = msgInfo->fields[i].wire_offset;
            const unsigned int array_length = msgInfo->fields[i].array_length;
            static const unsigned int array_buffer_length = (MAVLINK_MAX_PAYLOAD_LEN + MAVLINK_NUM_CHECKSUM_BYTES + 7);
            switch (msgInfo->fields[i].type) {
            case MAVLINK_TYPE_CHAR:
                if (array_length > 0) {
                    char* str = reinterpret_cast<char*>(m + offset);
                    // Enforce null termination
                    str[array_length - 1] = '\0';
                    QString v(str);
                    field->updateValue(v);
                } else {
                    // Single char
                    char b = *(reinterpret_cast<char*>(m + offset));
                    QString v(b);
                    field->updateValue(v);
                }
                break;
            case MAVLINK_TYPE_UINT8_T:
                if (array_length > 0) {
                    uint8_t* nums = m + offset;
                    QString tmp("%1, ");
                    QString string;
                    for (unsigned int j = 0; j < array_length - 1; ++j) {
                        string += tmp.arg(nums[j]);
                    }
                    string += QString::number(nums[array_length - 1]);
                    field->updateValue(string);
                } else {
                    // Single value
                    uint8_t u = *(m + offset);
                    field->updateValue(QString::number(u));
                }
                break;
            case MAVLINK_TYPE_INT8_T:
                if (array_length > 0) {
                    int8_t* nums = reinterpret_cast<int8_t*>(m + offset);
                    QString tmp("%1, ");
                    QString string;
                    for (unsigned int j = 0; j < array_length - 1; ++j) {
                        string += tmp.arg(nums[j]);
                    }
                    string += QString::number(nums[array_length - 1]);
                    field->updateValue(string);
                } else {
                    // Single value
                    int8_t n = *(reinterpret_cast<int8_t*>(m + offset));
                    field->updateValue(QString::number(n));
                }
                break;
            case MAVLINK_TYPE_UINT16_T:
                if (array_length > 0) {
                    uint16_t nums[array_buffer_length / sizeof(uint16_t)];
                    memcpy(nums, m + offset,  sizeof(uint16_t) * array_length);
                    QString tmp("%1, ");
                    QString string;
                    for (unsigned int j = 0; j < array_length - 1; ++j) {
                        string += tmp.arg(nums[j]);
                    }
                    string += QString::number(nums[array_length - 1]);
                    field->updateValue(string);
                } else {
                    // Single value
                    uint16_t n;
                    memcpy(&n, m + offset, sizeof(uint16_t));
                    field->updateValue(QString::number(n));
                }
                break;
            case MAVLINK_TYPE_INT16_T:
                if (array_length > 0) {
                    int16_t nums[array_buffer_length / sizeof(int16_t)];
                    memcpy(nums, m + offset,  sizeof(int16_t) * array_length);
                    QString tmp("%1, ");
                    QString string;
                    for (unsigned int j = 0; j < array_length - 1; ++j) {
                        string += tmp.arg(nums[j]);
                    }
                    string += QString::number(nums[array_length - 1]);
                    field->updateValue(string);
                } else {
                    // Single value
                    int16_t n;
                    memcpy(&n, m + offset, sizeof(int16_t));
                    field->updateValue(QString::number(n));
                }
                break;
            case MAVLINK_TYPE_UINT32_T:
                if (array_length > 0) {
                    uint32_t nums[array_buffer_length / sizeof(uint32_t)];
                    memcpy(nums, m + offset,  sizeof(uint32_t) * array_length);
                    QString tmp("%1, ");
                    QString string;
                    for (unsigned int j = 0; j < array_length - 1; ++j) {
                        string += tmp.arg(nums[j]);
                    }
                    string += QString::number(nums[array_length - 1]);
                    field->updateValue(string);
                } else {
                    // Single value
                    uint32_t n;
                    memcpy(&n, m + offset, sizeof(uint32_t));
                    //-- Special case
                    if(msg.msgid == MAVLINK_MSG_ID_SYSTEM_TIME) {
                        QDateTime d = QDateTime::fromMSecsSinceEpoch(static_cast<qint64>(n),Qt::UTC,0);
                        field->updateValue(d.toString("HH:mm:ss"));
                    } else {
                        field->updateValue(QString::number(n));
                    }
                }
                break;
            case MAVLINK_TYPE_INT32_T:
                if (array_length > 0) {
                    int32_t nums[array_buffer_length / sizeof(int32_t)];
                    memcpy(nums, m + offset,  sizeof(int32_t) * array_length);
                    QString tmp("%1, ");
                    QString string;
                    for (unsigned int j = 0; j < array_length - 1; ++j) {
                        string += tmp.arg(nums[j]);
                    }
                    string += QString::number(nums[array_length - 1]);
                    field->updateValue(string);
                } else {
                    // Single value
                    int32_t n;
                    memcpy(&n, m + offset, sizeof(int32_t));
                    field->updateValue(QString::number(n));
                }
                break;
            case MAVLINK_TYPE_FLOAT:
                if (array_length > 0) {
                    float nums[array_buffer_length / sizeof(float)];
                    memcpy(nums, m + offset,  sizeof(float) * array_length);
                    QString tmp("%1, ");
                    QString string;
                    for (unsigned int j = 0; j < array_length - 1; ++j) {
                       string += tmp.arg(static_cast<double>(nums[j]));
                    }
                    string += QString::number(static_cast<double>(nums[array_length - 1]));
                    field->updateValue(string);
                } else {
                    // Single value
                    float fv;
                    memcpy(&fv, m + offset, sizeof(float));
                    field->updateValue(QString::number(static_cast<double>(fv)));
                }
                break;
            case MAVLINK_TYPE_DOUBLE:
                if (array_length > 0) {
                    double nums[array_buffer_length / sizeof(double)];
                    memcpy(nums, m + offset,  sizeof(double) * array_length);
                    QString tmp("%1, ");
                    QString string;
                    for (unsigned int j = 0; j < array_length - 1; ++j) {
                        string += tmp.arg(nums[j]);
                    }
                    string += QString::number(static_cast<double>(nums[array_length - 1]));
                    field->updateValue(string);
                } else {
                    // Single value
                    double d;
                    memcpy(&d, m + offset, sizeof(double));
                    field->updateValue(QString::number(d));
                }
                break;
            case MAVLINK_TYPE_UINT64_T:
                if (array_length > 0) {
                    uint64_t nums[array_buffer_length / sizeof(uint64_t)];
                    memcpy(nums, m + offset,  sizeof(uint64_t) * array_length);
                    QString tmp("%1, ");
                    QString string;
                    for (unsigned int j = 0; j < array_length - 1; ++j) {
                        string += tmp.arg(nums[j]);
                    }
                    string += QString::number(nums[array_length - 1]);
                    field->updateValue(string);
                } else {
                    // Single value
                    uint64_t n;
                    memcpy(&n, m + offset, sizeof(uint64_t));
                    //-- Special case
                    if(msg.msgid == MAVLINK_MSG_ID_SYSTEM_TIME) {
                        QDateTime d = QDateTime::fromMSecsSinceEpoch(n/1000,Qt::UTC,0);
                        field->updateValue(d.toString("yyyy MM dd HH:mm:ss"));
                    } else {
                        field->updateValue(QString::number(n));
                    }
                }
                break;
            case MAVLINK_TYPE_INT64_T:
                if (array_length > 0) {
                    int64_t nums[array_buffer_length / sizeof(int64_t)];
                    memcpy(nums, m + offset,  sizeof(int64_t) * array_length);
                    QString tmp("%1, ");
                    QString string;
                    for (unsigned int j = 0; j < array_length - 1; ++j) {
                        string += tmp.arg(nums[j]);
                    }
                    string += QString::number(nums[array_length - 1]);
                    field->updateValue(string);
                } else {
                    // Single value
                    int64_t n;
                    memcpy(&n, m + offset, sizeof(int64_t));
                    field->updateValue(QString::number(n));
                }
                break;
            }
        }
    }
    rwlockMavlinkMessageDetail.unlock();
}

// 计算并更新MavLink消息频率
void StatusWidget::UpdateMavlinkMessageFrequency()
{
    QList<MavlinkMessage *> messages = scrollAreaContentMavlinkMessages->findChildren<MavlinkMessage *>();
//    qDebug() << messages.count();
    for(int i=0; i < messages.count(); i++)
    {
        MavlinkMessage *m = messages.at(i);
        m->lock.lockForWrite();
        if(m->count != m->lastCount){
            // >= 1Hz
            m->frequency = m->count - m->lastCount;
        }else{
            // < 1Hz
            float t = m->time.hour() * 3600 + m->time.minute() * 60
                    + m->time.second() + m->time.msec() / 1000.0f;
            float t0 = m->lastTime.hour() * 3600 + m->lastTime.minute() * 60
                    + m->lastTime.second() + m->lastTime.msec() / 1000.0f;
            m->frequency = 1.0f / (t - t0);
        }
        if(m->frequency > 10000.0f){
            // 时间戳还未更新，给个临时值防止出现无穷大
            m->frequency = 0.5;
        }
        m->updateText();
        m->lastCount = m->count;
        m->lock.unlock();
    }
}

void StatusWidget::toggleMavlinkMessageUpdateState()
{
    QPushButton* button = (QPushButton*)sender();
    if(button->isChecked()){
        // enable UI update
        button->setText(" MAVLINK MESSAGE UPDATE ENABLED");
        enableMavlinkMessageUpdate = true;
        timerUpdateMavlinkMessageFrequency->start();
    }else{
        // disable UI update
        button->setText(" MAVLINK MESSAGE UPDATE DISABLED");
        enableMavlinkMessageUpdate = false;
        timerUpdateMavlinkMessageFrequency->stop();
    }
}

// 在界面显示后，更新部分控件：3D模型、MavLink消息等
void StatusWidget::updateWidgetsAfterSetPage()
{
    addModelViewWidget();
}

// 当界面改变时，释放部分控件：3D模型、MavLink消息等
void StatusWidget::releaseWidgetsWhenPageChanged()
{
    removeModelViewWidget();
}

MavlinkMessage::MavlinkMessage(mavlink_message_t *msg, QWidget* parent)
{
    QString string;
    messageId = 0;
    componentId = 0;
    count = 1;
    name.clear();

    if(msg != nullptr){
        const mavlink_message_info_t* msgInfo = mavlink_get_message_info(msg);
        if (!msgInfo) {
            LOGE("MAVLinkMessage NULL msgInfo msgid(%d)", msg->msgid);
            qDebug() << QStringLiteral("MAVLinkMessage NULL msgInfo msgid(%1)").arg(msg->msgid);
            return;
        }
        name = QString(msgInfo->name);
        LOGI("New Message:", name.toStdString().data());
        messageId = msg->msgid;
        componentId = msg->compid;
        message = *msg;
    }else{
        LOGE("MAVLinkMessage msg is NULL!");
        qDebug() << QStringLiteral("MAVLinkMessage msg is NULL!");
        memset(&message, 0, sizeof (message));
    }
    string += QString::number(componentId);
    string += " 0Hz ";
    string += name;
    string += QString("(%1)").arg(messageId);

    this->setText(string);
    this->setMinimumHeight(40);
    this->setObjectName(name);
    this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
    this->setCheckable(true);
    QString stringStyleSHeet = "QPushButton {text-align:left;background-color:white;border: 1px solid #dcdfe6;padding: 10px;border-radius: 5px;}"
                                "QPushButton:hover {background-color: #ecf5ff;color: #409eff;}"
                                "QPushButton:pressed, QPushButton:checked {background-color: #96CBFF;border:1px solid #3a8ee6;color:white;}";
    this->setStyleSheet(stringStyleSHeet);
}

void MavlinkMessage::updateText(void)
{
    QString string;
    string += QString::number(componentId);
    string += " ";
    string += QString::number(frequency,'f',1);
    string += "Hz ";
    string += name;
    string += QString("(%1) ").arg(messageId);
    string += QString::number(count);
    this->setText(string);
}
